#pragma once
#include <iostream>
#include <string>
#include <cassert>
#include <cstring>
#include <stdlib.h>
#include <cerrno>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>

namespace server
{
    enum
    {
        USAGE_ERR = 1,
        SOCKET_ERR = 2,
        BIND_ERR = 3
    };
    static const std::string defaultIP = "0.0.0.0";
    class UdpServer
    {
    public:
        UdpServer(const uint16_t &port, const std::string ip = defaultIP) : _ip(ip), _port(port), _sockfd(-1)
        {
        }
        void initserver()
        {
            int ret = socket(AF_INET, SOCK_DGRAM, 0); // 系统部分
            // 参数1：选择域（本地还是，网络）； 参数2：选择流式服务还是，数据包服务，  参数3 ： 缺省为0即可。
            // 成功返回一个文件描述符
            // 失败返回-1，错误码被设置。
            // 在我看来，这里和打开文件没有区别 ， Linux一切皆文件。
            // 打开网卡和可开文件一样，
            // 未来读写网络就和读写文件一样了
            // assert(-1 != ret);
            if (ret == -1)
            {
                std::cout << "socket error: " << errno << ":" << strerror(errno) << std::endl;
                exit(SOCKET_ERR); // 退出码是自己设置的。
            }
            _sockfd = ret;
            // 现在我们需要告诉对于上面打开的服务，的端口号是多少，ip是多少

            struct sockaddr_in local;     // 在 用户栈 定义一个变量。
            bzero(&local, sizeof(local)); // 按字节为单位向一个内存空间写 0
            // 当然也可以memset，一样的。
            local.sin_family = AF_INET; // 填写协议家族。

            // local.sin_port = _port; // 如果给别人发消息，你的port和ip都要发给别人的。
            // 这个_port 可能不是大端，需要转换。
            local.sin_port = htons(_port);
            // htons（主机字节序转换位网络字节序）。

            // local.sin_addr.s_addr = inet_addr(_ip.c_str()); // 在网络中 IP 是uint32_t 表示的 整数。用户层面一般是点分十进制表示的字符串，。
            local.sin_addr.s_addr = INADDR_ANY; // 绑定任意端口。//INADDR_ANY这个任意端口其实就是 0.0.0.0
            // 需要转换 1. string -> uint32_t  2. htonl();
            // inet_addr(_ip.c_str());  //1和2 都给完成了 //将字符串风格的点分十进制转换为：uint32_t的网络字节序的整数。
            // C语言的结构体不能整体赋值。但是能整体被初始化。
            // c++ 结构体（类）的可以整体赋值。

            // 填写完成结构体之后，调用bind绑定
            //  绑定port 和 ip
            ret = bind(_sockfd, (struct sockaddr *)&local, sizeof(local)); // 网络部分
            if (ret == -1)
            {
                std::cout << "bind error: " << errno << ":" << strerror(errno) << std::endl;
                exit(BIND_ERR);
            }
        }

        void startserver()
        {
            // 网络服务器的本质就是一个死循环。
            // 一般死循环的进程（服务器），叫做常驻内存的进程，要避免内存泄漏的问题。这个很严重。
            // 只有常驻内存的进程，才去考虑内存泄漏的问题。
            // 操作系统（OS）本质就是一个死循环。常驻内存。
            // 死循环对外提供服务。
            for (;;)
            {
                char buf[1024];
                sleep(1);
                struct sockaddr_in peer;
                socklen_t len = sizeof(peer);
                memset(&peer, 0, len);
                // 从网络sock读取数据。存入buf
                size_t s = recvfrom(_sockfd, buf, sizeof(buf) - 1, 0, (struct sockaddr *)&(peer), &len);
                // 读取完成
                if (s > 0)
                {
                    buf[s] = 0; // 当作字符串读取。
                    std::string message = buf;
                    std::string clientip = inet_ntoa(peer.sin_addr); // 1.网络字节序 -> 主机字节序 ；2. uint32_t -> string
                    uint16_t clientport = ntohl(peer.sin_port);
                    std::cout << clientip << "[" << clientport << "]#"
                              << " message: " << message << std::endl;
                }
            }
        }

        ~UdpServer()
        {
        }

    private:
        uint16_t _port;  // 保存当前服务器端口port号
        std::string _ip; // 保存当前服务器ip
        int _sockfd;     // 保存打开网卡对应的sockfd
    };

}

// #pragma once

// #include <iostream>
// #include <string>
// #include <strings.h>
// #include <cerrno>
// #include <cstring>
// #include <cstdlib>
// #include <functional>
// #include <unistd.h>
// #include <sys/types.h>
// #include <sys/socket.h>
// #include <arpa/inet.h>
// #include <netinet/in.h>

// namespace Server
// {
//     using namespace std;

//     static const string defaultIp = "0.0.0.0"; //TODO
//     static const int gnum = 1024;

//     enum {USAGE_ERR = 1, SOCKET_ERR, BIND_ERR};

//     typedef function<void (string,uint16_t,string)> func_t;

//     class udpServer
//     {
//     public:
//         udpServer(const func_t &cb, const uint16_t &port, const string &ip = defaultIp)
//         :_callback(cb), _port(port), _ip(ip), _sockfd(-1)
//         {}
//         void initServer()
//         {
//             // 1. 创建socket
//             _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
//             if(_sockfd == -1)
//             {
//                 cerr << "socket error: " << errno << " : " << strerror(errno) << endl;
//                 exit(SOCKET_ERR);
//             }
//             cout << "socket success: " << " : " << _sockfd << endl;

//             // 2. 绑定port，ip(TODO)
//             // 未来服务器要明确的port，不能随意改变
//             struct sockaddr_in local; // 定义了一个变量，栈，用户
//             bzero(&local, sizeof(local));
//             local.sin_family = AF_INET;
//             local.sin_port = htons(_port); // 你如果要给别人发消息，你的port和ip要不要发送给对方
//             local.sin_addr.s_addr = inet_addr(_ip.c_str());   // 1. string->uint32_t 2. htonl(); -> inet_addr
//             //local.sin_addr.s_addr = htonl(INADDR_ANY); // 任意地址bind，服务器的真实写法
//             int n = bind(_sockfd, (struct sockaddr*)&local, sizeof(local));
//             if(n == -1)
//             {
//                 cerr << "bind error: " << errno << " : " << strerror(errno) << endl;
//                 exit(BIND_ERR);
//             }
//             // UDP Server 的预备工作完成
//         }
//         void start()
//         {
//             // 服务器的本质其实就是一个死循环
//             char buffer[gnum];
//             for(;;)
//             {
//                 // 读取数据
//                 struct sockaddr_in peer;
//                 socklen_t len = sizeof(peer); //必填
//                 ssize_t s = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&peer, &len);
//                 // 1. 数据是什么 2. 谁发的？
//                 if(s > 0)
//                 {
//                     buffer[s] = 0;
//                     string clientip = inet_ntoa(peer.sin_addr); //1. 网络序列 2. int->点分十进制IP
//                     uint16_t clientport = ntohs(peer.sin_port);
//                     string message = buffer;

//                     cout << clientip <<"[" << clientport << "]# " << message << endl;
//                     // 我们只把数据读上来就完了吗？对数据做处理
//                     _callback(clientip, clientport, message);
//                 }
//             }
//         }
//         ~udpServer()
//         {
//         }
//     private:
//         uint16_t _port;
//         string _ip; // 实际上，一款网络服务器，不建议指明一个IP
//         int _sockfd;
//         func_t _callback; //回调
//     };
// }